/*
 * @Author: Baishui
 * @Date: 2021-09-23 20:41:39
 * @LastEditors: Baishui
 * @LastEditTime: 2021-09-28 20:29:36
 * @Description: file content
 */
import axios, { AxiosRequestConfig, AxiosInstance, AxiosPromise, CancelTokenStatic } from 'axios'
import { omit, merge } from 'lodash-es'
import { message } from 'ant-design-vue'

type RequestConfig = Record<string, string | any>

interface RequestOption extends AxiosRequestConfig {
  newInstance?: boolean
  loadingShow?: boolean
}
interface PendingItem {
  u: string
  f: () => void
}

const ignoreProps = ['newInstance', 'loadingShow']

class HttpRequest {
  private readonly baseUrl: string
  private instance: AxiosInstance
  private pending: Array<PendingItem>
  private cancelToken: CancelTokenStatic

  constructor() {
    this.baseUrl = (import.meta.env.VITE_BASEURL_API as string) || ''
    this.instance = this.getInstance()
    this.pending = [] // 请求列表
    this.cancelToken = axios.CancelToken
  }
  private getBaseConfig(): AxiosRequestConfig {
    return {
      baseURL: this.baseUrl,
      headers: {
        'Content-Type': 'application/json',
      },
      timeout: 1000 * 10,
    }
  }

  // 获取实例
  private getInstance(option: AxiosRequestConfig = {}): AxiosInstance {
    const newOption = merge({}, option, this.getBaseConfig())
    const instance = axios.create(newOption)
    this.interceptors(instance)
    return instance
  }

  // get请求
  get(url: string, data: RequestConfig = {}, option: RequestOption = {}): AxiosPromise {
    const config = {
      url,
      params: data,
      method: 'get',
    }
    return this.request(config, option)
  }

  // post请求
  post(url: string, data: RequestConfig = {}, option: RequestOption = {}): AxiosPromise {
    const config = {
      url,
      data,
      method: 'post',
    }
    return this.request(config, option)
  }

  // newInstance 是否新建实例
  request(config: RequestConfig, option: RequestOption): AxiosPromise {
    const { newInstance } = option
    const _option = omit(option, ignoreProps) // 过滤非axios属性

    const instance = newInstance ? this.getInstance(_option) : this.instance

    return instance(config)
  }

  // 请求拦截器
  private interceptors(instance: AxiosInstance): void {
    instance.interceptors.request.use(
      (config) => {
        this.removePeding(config)
        this.setCancelToken(config)
        return config
      },
      (err) => {
        return Promise.reject(err.resopnse.data)
      }
    )
    instance.interceptors.response.use(
      (res) => {
        this.removePeding(res.config)

        const { data } = res
        // if (data.code === 0) {
        //   return res.data
        // }
        // 接口鉴权失败等处理
        if (data.code === '10005') {
          // store.commit('logout')
          return Promise.reject(new Error('logout'))
        }
        return data.result
      },
      (err) => {
        message.error(err.response.data || '服务器错误，请刷新重新')
        // 接口报500错误处理
        if (err.response.status.toString().startsWith('5')) {
          return Promise.reject(new Error('服务器错误，请刷新重新'))
        }
        return Promise.reject(err.response.data)
      }
    )
  }

  // 移除pending列表
  private removePeding(config: RequestConfig) {
    this.pending.forEach((p, i) => {
      if (p.u === `${config.url}&${config.method}`) {
        p?.f()
        this.pending.splice(i, 1)
      }
    })
  }

  // 设置cancleToken
  private setCancelToken(config: RequestConfig) {
    config.cancelToken = new this.cancelToken((c) => {
      this.pending.push({
        u: `${config.url}&${config.method}`,
        f: c,
      })
    })
  }
}

const instance = new HttpRequest()

export default instance
